/* 
 * File:   IPToolkit.cpp
 * Author: liuyi
 * 
 * Created on August 20, 2009, 4:28 PM
 */

#include "IPToolkit.h"

#include <stack>
#include <cmath>
#include <cassert>
#include <cstring>

using namespace std;

void IPToolkit::fillConnetecd(uchar *imageData, int width, int height, const Point seed, uchar newValue) {
    uchar oldValue = *(imageData + width*seed.y + seed.x);
    if(oldValue == newValue) {
        return;
    }

    stack<PointPair *> list;

    *(imageData + width*seed.y + seed.x) = newValue;
    list.push(new PointPair(seed, Point(-1, -1)));

    PointPair *pp = NULL;
    Point p, dir;
    bool go;
    while(!list.empty()) {
        pp = list.top();
        p = pp->p;
        dir = pp->dir;

        go = true;
        while (go) {
            while (go && (p.x + dir.x < 0 || p.x + dir.x >= width || p.y + dir.y < 0 || p.y + dir.y >= height)) {
                go = nextDir(dir);
            }

            if (!go || *(imageData + width * (p.y + dir.y) + (p.x + dir.x)) == oldValue) {
                break;
            }

            go = nextDir(dir);
        }

        if(!go) {
            delete pp;
            list.pop();

            continue;
        }

        *(imageData + width*(p.y + dir.y) + (p.x + dir.x)) = newValue;
        list.push(new PointPair(Point(p.x + dir.x, p.y + dir.y), Point(-1, -1)));
    }
}

void IPToolkit::resize(uchar *dst, int dW, int dH, const uchar *src, int sW, int sH, Intrplt method) {
    if (method == NEAREST) {
        int x, y;
        for (int i = 0; i < dH; i++) {
            y = round((sH - 1) * i * 1.0f / (dH - 1));

            for (int j = 0; j < dW; j++) {
                x = round((sW - 1) * j * 1.0f / (dW - 1));

                *(dst + dW * i + j) = *(src + sW * y + x);
            }
        }
    } else if (method == LINER) {
        float u, v, tu, tv;
        const uchar *pix = NULL;
        for (int i = 0; i < dH; i++) {
            tv = (sH - 1) * i * 1.0f / (dH - 1);
            v = tv - int(tv);

            for (int j = 0; j < dW; j++) {
                tu = (sW - 1) * j * 1.0f / (dW - 1);
                u = tu - int(tu);

                pix = src + sW * int(tv) + int(tu);
                if(v == 0 && u > 0) {
                    *(dst + dW * i + j) = *pix * (1 - u)*(1 - v) + *(pix + 1) * u * (1 - v);
                } else if(v > 0 && u == 0) {
                    *(dst + dW * i + j) = *pix * (1 - u)*(1 - v) + *(pix + sW)*(1 - u) * v;
                } else if(v ==0 && u == 0) {
                    *(dst + dW * i + j) = *pix;
                } else {
                    *(dst + dW * i + j) = *pix * (1 - u)*(1 - v) + *(pix + 1) * u * (1 - v) + *(pix + sW)*(1 - u) * v + *(pix + sW + 1) * u*v;
                }
            }
        }
    }
}

void IPToolkit::binarize(uchar *imageData, int width, int height, uchar theta) {
    int size = width * height;
    for(int i = 0; i < size; i++) {
        imageData[i] = (imageData[i] < theta)? 0: 255;
    }
}

void Morphology::erode(uchar *dst, const uchar *src, int width, int height, StructElem &elem, int iteration) {
    uchar *temp = NULL;
    if(dst == src) {
        temp = new uchar[width*height];
    } else {
        temp = dst;
    }

    memset(temp, m_bg, width*height*sizeof(uchar));

    const uchar *ps = NULL;
    uchar *pe = elem.data, *ppe = NULL;
    const uchar *pps = NULL;

    int rows = elem.rows;
    int cols = elem.cols;
    int th = height - rows + 1;
    int tw = width - cols + 1;

    int l, k;
    for (int i = 0; i < th; i++) {
        ps = src + width * i;
        
        for(int j = 0; j < tw; j++, ps++) {
            for(k = 0; k < rows; k++) {
                pps = ps + width*k;
                ppe = pe + cols*k;

                for(l = 0; l < cols && (*pps == m_front || *ppe == *pps); l++, ppe++, pps++) {
                }

                if(l != cols) {
                    break;
                }
            }

            if(l == cols) {
                *(temp + width*(i + elem.anchor.y) + j + elem.anchor.x) = m_front;
            }
        }
    }

    if(dst == src) {
        memcpy(dst, temp, width*height*sizeof(uchar));
        delete []temp;
    }

    if(--iteration > 0) {
        erode(dst, src, width, height, elem, iteration);
    }
}

void Morphology::dilate(uchar *dst, const uchar *src, int width, int height, Morphology::StructElem &elem, int iteration) {
    uchar *temp = NULL;
    if(dst == src) {
        temp = new uchar[width*height];
    } else {
        temp = dst;
    }

    memset(temp, m_bg, width*height*sizeof(uchar));

    const uchar *ps = NULL;
    uchar *pd = NULL;
    uchar *pe = elem.data, *ppe = NULL, *ppd = NULL;

    int rows = elem.rows;
    int cols = elem.cols;
    int th = height - rows + 1;
    int tw = width - cols + 1;
    int aX = elem.anchor.x;
    int aY = elem.anchor.y;

    int l, k;
    for (int i = 0; i < th; i++) {
        ps = src + width*(i + aY) + aX;
        pd = temp + width*i;

        for(int j = 0; j < tw; j++, ps++, pd++) {
            if(*ps == m_front) {
                for (k = 0; k < rows; k++) {
                    ppd = pd + width*k;
                    ppe = pe + cols*k;
                    
                    for (l = 0; l < cols; l++, ppd++, ppe++) {
                        if(*ppe == m_front) {
                            *ppd = m_front;
                        }
                    }
                }
            }
        }
    }

    if(dst == src) {
        memcpy(dst, temp, width*height*sizeof(uchar));
        delete []temp;
    }

    if(--iteration > 0) {
        dilate(dst, src, width, height, elem, iteration);
    }
}